from tos import *
from threading import *
from time import time 

class SyncAM(AM):

    serial_access = Lock()

    read_access = Condition()
    latest_packet = None
    got_packet = False
    reading = False
    writers = 0

    def __init__(self, s=None, read_timeout=1):
        print "Don't forget export MOTECOM=serial@/dev/tty.usbserial-XXX:57600"
        super(SyncAM, self).__init__(s, self.set_packet)
        self.read_timeout = read_timeout
    
    def set_packet(self, packet):
        """
        Called from super class whenever a packet is received
        """
        if packet!=None:
            with self.read_access:
                #print "setting packet: ", packet
                self.got_packet = True
                self.latest_packet = packet
                self.reading = False
                self.read_access.notifyAll()
        return packet

    # TODO: add timeout parameter:
    # def read(self, timeout=None):
    def read(self, timeout=None):
        """
        Reads a packet from the serial. This method is thread safe.
        """

        end = None
        if timeout: end = time() + timeout;

        self.read_access.acquire()
        if not self.reading:
            #print "read first"
            self.reading = True
            self.got_packet = False
            self.read_access.release()
            while True:
                with self.read_access:
                    if self.got_packet:
                        #print "got packet, stop reading"
                        break
                    if self.writers>0:
                        #print "waiting for writers"
                        continue

                with self.serial_access:
                    #print "read serial access acquired"
                    # set packet will be called with result
                    pkt = super(SyncAM, self).read(self.read_timeout)
                    #print "read serial access released"

                if pkt!=None or (end and time() >= end):
                    #print "returned packet, stop reading"
                    break

        else:
            #print "read another, so wait"
            self.read_access.wait(timeout)
            #print "notified, so continue"
            self.read_access.release()

        if end and time()>=end:
            return None
        else:
            return self.latest_packet
    

    # def write(self, packet, amId, timeout=None, blocking=True):
    def write(self, packet, amId, timeout=None, blocking=True):
        """
        Write a packet to the serial. This method is thread safe. A
        write can be delayed up to self.read_timeout second due to
        pending pending reads
        """
        #print "### writing waiting for access"
        with self.read_access:
            self.writers = self.writers + 1

        with self.serial_access:
            #print "### writing start"
            r = super(SyncAM, self).write(packet, amId, timeout, blocking)
            #print "### writing done"

        with self.read_access:
            self.writers = self.writers - 1

        return r
